package system

import (
	"context"
	"gf_blog_admin/internal/dao"
	"gf_blog_admin/internal/model"
	"gf_blog_admin/internal/service"
	"gf_blog_admin/utility/casbinPolicy"
	"gf_blog_admin/utility/utils"
	"github.com/gogf/gf/v2/database/gdb"
	"github.com/gogf/gf/v2/errors/gerror"
)

type sSysMenu struct{}

func init() {
	service.RegisterSysMenu(NewSysMenu())
}

func NewSysMenu() *sSysMenu {
	return &sSysMenu{}
}

func (s *sSysMenu) List(ctx context.Context) (out []*model.MenuItem, err error) {
	var (
		cls = dao.BlSysMenu.Columns()
		m   = dao.BlSysMenu.Ctx(ctx)
	)

	out = make([]*model.MenuItem, 0)
	err = m.Fields(
		cls.Id, cls.DisplayName, cls.MenuName, cls.MenuPath, cls.Component, cls.MenuIcon,
		cls.MenuSort, cls.ParentId, cls.MenuGenre, cls.MenuHidden, cls.MenuStatus,
	).Scan(&out)
	return
}

func (s *sSysMenu) TreeList(ctx context.Context) ([]*model.MenuTreeItem, error) {
	menus, err := s.List(ctx)
	if err != nil {
		return nil, err
	}

	return s.formTree(0, menus, 1), nil
}

func (s *sSysMenu) Detail(ctx context.Context, id uint64) (menu *model.MenuItem, err error) {
	err = dao.BlSysMenu.Ctx(ctx).Where(dao.BlSysMenu.Columns().Id, id).Scan(&menu)
	if err != nil {
		return nil, err
	}
	if menu == nil {
		return nil, gerror.New("菜单记录不存在")
	}
	return
}

func (s *sSysMenu) Store(ctx context.Context, in *model.MenuFormInput) (err error) {
	cls := dao.BlSysMenu.Columns()

	if err = utils.IsUnique(ctx, &dao.BlSysMenu, map[string]string{
		cls.MenuName: in.MenuName,
	}, "菜单名已存在"); err != nil {
		return err
	}

	err = dao.BlSysMenu.Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
		_, err = dao.BlSysMenu.Ctx(ctx).Data(in).Insert()
		return err
	})
	return
}

func (s *sSysMenu) Update(ctx context.Context, id uint64, in *model.MenuFormInput) (err error) {
	cls := dao.BlSysMenu.Columns()

	if err = utils.IsUnique(ctx, &dao.BlSysMenu, map[string]interface{}{
		cls.Id + " !=": id,
		cls.MenuName:   in.MenuName,
	}, "菜单名已存在"); err != nil {
		return err
	}

	if _, err = s.Detail(ctx, id); err != nil {
		return err
	}

	err = dao.BlSysMenu.Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
		_, err = dao.BlSysMenu.Ctx(ctx).Where(cls.Id, id).Data(in).Update()
		return err
	})
	return
}

func (s *sSysMenu) Destroy(ctx context.Context, ids []uint64) (err error) {
	var (
		check bool
		rules []string
		cls   = dao.BlSysMenu.Columns()
		m     = dao.BlSysMenu.Ctx(ctx)
	)

	ids = utils.UniqueIds(ids)
	if check, err = m.WhereIn(cls.ParentId, ids).Exist(); err != nil {
		return err
	} else if check {
		return gerror.New("请先删除该菜单下的所有菜单！")
	}

	menus, err := m.WhereIn(cls.Id, ids).Fields(cls.MenuName).Array()
	if err != nil {
		return err
	}
	if menus == nil || len(menus) == 0 {
		return gerror.New("数据记录不存在")
	}
	for _, menu := range menus {
		rules = append(rules, menu.String())
	}
	err = dao.BlSysMenu.Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
		if _, err = m.WhereIn(cls.Id, ids).Delete(); err != nil {
			return err
		}
		if err = s.detachRule(rules); err != nil {
			return err
		}
		return nil
	})
	return
}

func (s *sSysMenu) SelectMenuTree(ctx context.Context) (menus []*model.SelectMenuTreeItem, err error) {
	var (
		cls  = dao.BlSysMenu.Columns()
		list = make([]*model.SelectMenuItem, 0)
	)

	err = dao.BlSysMenu.Ctx(ctx).
		Fields(cls.Id, cls.DisplayName, cls.MenuName, cls.ParentId).
		Scan(&list)
	if err != nil {
		return nil, err
	}

	menus = s.formSelectTree(0, list)
	return
}

// 选项结构转换为树形结构
func (s *sSysMenu) formSelectTree(pid uint64, nodes []*model.SelectMenuItem) []*model.SelectMenuTreeItem {
	tree := make([]*model.SelectMenuTreeItem, 0)
	for _, v := range nodes {
		if v.ParentId == pid {
			child := s.formSelectTree(v.Id, nodes)
			item := &model.SelectMenuTreeItem{
				SelectMenuItem: v,
				Children:       child,
			}
			tree = append(tree, item)
		}
	}
	return tree
}

// 列表结构转换为树形结构
func (s *sSysMenu) formTree(pid uint64, nodes []*model.MenuItem, level uint) []*model.MenuTreeItem {
	tree := make([]*model.MenuTreeItem, 0)
	for _, v := range nodes {
		if v.ParentId == pid {
			child := s.formTree(v.Id, nodes, level+1)
			item := &model.MenuTreeItem{
				MenuItem:  v,
				MenuLevel: level,
				Children:  child,
			}
			tree = append(tree, item)
		}
	}
	return tree
}

// 删除菜单权限
func (s *sSysMenu) detachRule(rules []string) error {
	_, err := casbinPolicy.Enforcer.DeletePermission(rules...)
	return err
}
